import numpy as np
from sympy import *
from Utils import ZouUtils
import Utils


def dataGenerator():
    _x1, _x2 = symbols('x1 x2')
    _f_x = _x1 ** 2 + _x2 ** 2 - 2 * _x1 - 4 * _x2 + 3
    _x = np.array((0, 0)).reshape((2, 1))
    _A = np.array([[-2, 1],
                   [-1, -1],
                   [1, 0],
                   [0, 1]])
    _b = np.array([-1, -2, 0, 0]).reshape((4, 1))
    return {
        'f_x': _f_x,
        'A': _A,
        'x': _x,
        'b': _b,
    }


if __name__ == '__main__':
    data = dataGenerator()
    f_x = data['f_x']
    A = data['A']
    b = data['b']
    x = data['x']
    epsilon1 = 0.001
    epsilon2 = 0.001
    l = symbols('l')
    print('Zoutendijk可行向量法演示')
    print('-' * 40)
    print('初始数据:')
    print('\t\t目标函数为：')
    print('\t\tf(x)={}'.format(f_x))
    print('\t\t非有效约束为的矩阵为\n{}'.format(np.append(A, b, axis=1)))
    print('-' * 40)
    cnt = 0
    while True:
        x=Utils.correctX(x)
        print('第{}次迭代，x={}，f(x)={}'.format(cnt, x.T, f_x.evalf(subs=Utils.list2dict(x))))
        print('-'*40)
        cnt = cnt + 1
        division = ZouUtils.saperateMatrix(A, x, b)
        grad = ZouUtils.getGradient(f_x, x)
        A1 = division['A1']
        A2 = division['A2']
        b2 = division['b2']
        if ZouUtils.isInner(A1, E):
            if np.sqrt(np.dot(grad, grad)) < epsilon1:
                break
            else:
                d = -grad.reshape((len(x), 1))
        else:
            nums = len(division['EI'])
            scale = [(-1, 1) for i in range(nums)]
            d = Utils.Simplex(grad, -A1, np.zeros((nums, 1)), scale)['x']
            d = np.array(d).reshape((len(x), 1))
        z = np.matmul(grad, d).sum()
        if (abs(z) < epsilon2):
            break
        lambdaMax = ZouUtils.getMaxLambda(b2, A2, x, d)
        xl = x + l * d
        xl = Utils.list2dict(xl)
        f_lambda = f_x.evalf(subs=xl)
        x = x + Utils.getMinPoint(f_lambda, lambdaMax, l) * d
    print('最小值在{}处取得，最小值为{}'.format(x.T, f_x.evalf(subs=Utils.list2dict(x))))
